package com.itany.shop.controller;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author:石小俊
 * @Date: 2022/12/23 9:05
 * @Version:1.0-SNAPSHOT
 * @Description:
 */
@WebServlet("*.file")
public class FileServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = request.getServletPath();
        if("/upload.file".equals(path)){
            upload(request,response);
        }
        if("/download.file".equals(path)){
            download(request,response);
        }
        if("/download2.file".equals(path)){
            download2(request,response);
        }
    }

    public void download(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 找到需要下载的资源地址
        String path = getServletContext().getRealPath("/resources/jstl-1.2.jar");

        // 使用IO进行文件的复制操作
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(path);
            out = response.getOutputStream();

            // 文件下载遵循的MIME协议
            // 在使用浏览器进行文件下载时,如果没有指定附件方式,默认会根据浏览器的配置而使用对应的附件方式
            // 可以手动在响应头中指定附件方式
            // 在响应时,消息头中有一个控制附件方式的属性:content-disposition
            // 该属性的值有两种
            // 1.attachment:以附件的方式下载
            // 2.inline:在线打开
//            response.setHeader("content-disposition","attachment");
            // 该属性除了设置附件方式以外,还可以指定文件名
            // 属性值格式:附件方式;filename=文件名
//            response.setHeader("content-disposition","attachment;filename=jstl-1.2.jar");
            // 如果文件名存在中文,需要将其转换成ISO-8859-1的字符集才会被识别
            response.setHeader("content-disposition",new String("attachment;filename=中文.jar".getBytes(),"ISO-8859-1"));

            byte[] b = new byte[10240];
            int i;
            while((i = in.read(b)) != -1){
                out.write(b,0,i);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void download2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 找到需要下载的资源地址
        String path = getServletContext().getRealPath("/js/jquery-3.4.1.min.js");

        // 使用IO进行文件的复制操作
        FileInputStream in = null;
        OutputStream out = null;
        try {
            in = new FileInputStream(path);
            out = response.getOutputStream();

            response.setHeader("content-disposition","inline");

            byte[] b = new byte[10240];
            int i;
            while((i = in.read(b)) != -1){
                out.write(b,0,i);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 该方式只适用于获取表单数据,而不适用于二进制流数据
//        String username = request.getParameter("username");
//        String file = request.getParameter("file");
//        System.out.println("username:"+username+",file:"+file);

        // 如果需要获取二进制流数据,我们需要从请求中直接获取二进制流数据
//        InputStream in = request.getInputStream();
//        byte[] b = new byte[10240];
//        int i = in.read(b);
//        System.out.println(new String(b,0,i));
        // 到这一步,文件上传需要的数据已经全部获取到
        // 只是尚未对这些数据进行解析
        // 这些数据我们需要区分是否是文件,如果是文件需要将其复制一份到服务器中
        // 这些解析的操作相对比较麻烦

        // 我们可以使用apache提供的第三方工具进行解析
        // commons-fileupload工具只做解析操作
        // 该工具依赖commons-io
        // 1.获取解析工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 2.使用该解析工厂生成对应的解析器
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            // 3.开始解析,解析后返回的是一个FileItem的集合
            // 该集合中存放的是解析的每一个数据对象
            // 相当于表单数据时请求中的每一个元素
            List<FileItem> fileItems = upload.parseRequest(request);
            Map<String,FileItem> items = new HashMap<>();
            System.out.println("元素个数:"+fileItems.size());
            for(FileItem item : fileItems){
//                System.out.println("表单元素name属性:"+item.getFieldName());
//                if("username".equals(item.getFieldName())){
//                    System.out.println("当前元素是一个用户名模块");
//                    // 获取对应的数据
//                    System.out.println("用户名:"+item.getString("utf-8"));
//                }
//                if("file".equals(item.getFieldName())){
//                    System.out.println("当前元素是一个文件模块");
//                    System.out.println("文件内容:"+item.getString("utf-8"));
//                }
                // 将List集合中的每一个元素加入到Map集合中
                // key为对应模块的name属性值,value为当前遍历的FileItem
                items.put(item.getFieldName(),item);
            }
            System.out.println("用户名:"+items.get("username").getString("utf-8"));
            System.out.println("文件内容:"+items.get("file").getString("utf-8"));
            FileItem file = items.get("file");

            // 在上传时,我们可能需要对上传的文件格式进行限定
            // 假设此时不允许上传jsp文件到服务器中,如何实现?
            // 获取当前上传的文件的文件名
            // 如果该文件以.jsp结尾,说明上传的是jsp文件
            String fileName = file.getName();
            if(fileName.endsWith(".jsp")){
                request.setAttribute("uploadMsg","对不起,上传失败,不允许上传jsp文件");
                request.getRequestDispatcher("/jsp/upload.jsp").forward(request,response);
                return;
            }



            // 将文件上传到服务器中
            // 在服务器根目录创建一个专门用于上传图片的目录:upload
            // 在该目录中,每天创建一个子目录,目录名为当天的年月日
            String path = getServletContext().getRealPath("/upload/"+new SimpleDateFormat("yyyyMMdd").format(new Date()));
            // 创建指定path的目录
            File f = new File(path);
            f.mkdirs();

            // 在上传时,不同用户上传的文件可能会出现重名的
            // 在同一个文件夹中,如果出现同名文件会进行覆盖
            // 为了避免出现这个情况,我们可以为每一个上传的文件取一个永不重复的别名
            // 为文件加一个毫秒数的后缀

            fileName = fileName.substring(0,fileName.lastIndexOf("."))+"-"+new Date().getTime()+fileName.substring(fileName.lastIndexOf("."));

            // 将文件上传到服务器对应位置
            // 在FileItem对象中提供了对应的方法write
            // fileItem.write(file):表示将源文件fileItem复制到目标文件file中
            file.write(new File(path,fileName));
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
